package com.ljwm.seckill.service.impl;

import com.ljwm.seckill.dao.SeckillDao;
import com.ljwm.seckill.dao.SuccessKilledDao;
import com.ljwm.seckill.dto.Exposer;
import com.ljwm.seckill.dto.SeckillExecution;
import com.ljwm.seckill.entity.Seckill;
import com.ljwm.seckill.entity.SuccessKilled;
import com.ljwm.seckill.enums.SeckillState;
import com.ljwm.seckill.exception.RepeatKillException;
import com.ljwm.seckill.exception.SeckillCloseException;
import com.ljwm.seckill.exception.SeckillException;
import com.ljwm.seckill.service.SeckillService;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.List;

/**
 * Created by yuzhou on 2017/8/6.
 */
// @Component
@Service
public class SeckillServiceImpl implements SeckillService {

  private static final Logger _log = LoggerFactory.getLogger(SeckillServiceImpl.class);
  // 盐值
  private static final String salt = "dfsafjkjoijqerfdfkdj980{P{POpdfdf";

  @Autowired //@Resource
  private SeckillDao seckillDao;

  @Autowired
  private SuccessKilledDao successKilledDao;

  private String getMD5(Long seckillId) {
    String base = seckillId + "/" + salt;
    String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
    return md5;
  }

  @Override
  public List<Seckill> getSeckillList() {
    return seckillDao.queryAll(0, 4);
  }

  @Override
  public Seckill getById(Long seckillId) {
    return seckillDao.queryById(seckillId);
  }

  @Override
  public Exposer exportSeckillUrl(Long seckillId) {
    Seckill seckill = seckillDao.queryById(seckillId);
    if (seckill == null) {
      return new Exposer(false, seckillId);
    }

    Date startTime = seckill.getStartTime();
    Date endTime = seckill.getEndTime();

    Date now = new Date();

    if(now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime()) {
      return new Exposer(false, seckillId, now.getTime(), startTime.getTime(), endTime.getTime());
    }

    String md5 = getMD5(seckillId);
    return new Exposer(true, md5, seckillId);
  }

  @Override
  @Transactional
  public SeckillExecution executeSeckill(Long seckillId, Long userPhone, String md5)
    throws SeckillException, RepeatKillException, SeckillCloseException {

    try {
      if (md5 == null || !md5.equals(getMD5(seckillId))) {
        throw new SeckillException("seckill data rewrite");
      }

      //执行秒杀逻辑：减库存 + 记录购买行为
      Date now = new Date();
      int updated = seckillDao.reduceNumber(seckillId, now);

      if (updated <= 0) { //没有更新到记录，秒杀结束
        throw new SeckillCloseException("seckill is closed");
      } else { //记录购买行为
        int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
        if (insertCount <= 0) {
          // 重复秒杀
          throw new RepeatKillException("repeat seckill");
        } else {
          SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
          return new SeckillExecution(seckillId, SeckillState.SUCCESS, successKilled);
        }
      }
    } catch (SeckillCloseException e1) {
      throw e1;
    } catch (RepeatKillException e2) {
      throw e2;
    } catch (Exception e) {
      _log.error(e.getMessage(), e);
      throw new SeckillException("seckill inner error: " + e.getMessage());
    }
  }
}
